package com.weng.cloud.service8881.jdk8;

import com.weng.cloud.commons.base.JsonUtil;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;

import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * @Author: weng
 * @Date: 2022/5/8
 * @Description: 说真的,很像 flink stream api ...
 *
 * @see <a href="https://www.runoob.com/java/java8-streams.html">【RUNOOB】Java 8 Stream</a>
 * @see <a href="https://blog.csdn.net/superfjj/article/details/105722611">【CSDN】java stream中Collectors的用法</a>
 * @see <a href="https://blog.csdn.net/Lou_Lan/article/details/120362011">【CSDN】java8 Stream的flatMap的使用</a>
 * @see <a href="https://www.jb51.net/article/218287.htm">【JB51】Java8的Stream()与ParallelStream()的区别说明</a>
 *
 * @see <a href="https://blog.csdn.net/qq_38200877/article/details/121230706">java 8 的stream流的reduce总结</a>
 * @see <a href="https://blog.csdn.net/lydms/article/details/123434013">Comparator使用简介</a>
 */

public class TestStreamApi {

    private final static List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl","bc");
    private final static List<Person> persons = Arrays.asList(
            Person.builder().no(6).name("james").build(),
            Person.builder().no(8).name("williams").build(),
            Person.builder().no(7).name("kd").build(),
            Person.builder().no(8).name("kobe").build(),
            Person.builder().no(35).name("kd").build()
    );

    public static void main(String[] args) {

        //testForEach(testMap());
//        testForEach(strings.stream());

//        testFilter();
//        testForEach(strings.stream());
        //testForEach(testFilter());

        //testIntStream().forEach(System.out::println);

//        testCollect();

        try {
            testReduce();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * <br/>function: 没有返回值
     * <br/>tip:
     *
     * @param: [stream]
     * @return: void
     * @author: weng
     * @date: 2022/5/8 11:48
     */
    static void testForEach(Stream<String> stream){
        stream.forEach(System.out::println);
    }

    static Stream<String> testMap(){
        // 返回的是副本
        return strings
                .stream()
                .map(v->"map前缀 -> "+v)
        ;
    }

    static Stream<String> testFilter(){
        return strings
                .stream()
                // :: 相当于 (ele) -> {},即lambda表达式
                // 相当于需要一个 boolean 作为 filter 的参数
                .filter(StringUtils::isNotBlank)
        ;
    }

    /**
     * <br/>function: IntStream 概念类似 Stream
     * <br/>tip: 实际并不是stream子类,基本类型还是有些区别的
     *
     * All Superinterfaces:
     *  AutoCloseable， BaseStream < Integer， IntStream >
     *
     * @param: []
     * @return: java.util.stream.IntStream
     * @author: weng
     * @date: 2022/5/8 11:57
     */
    static IntStream testIntStream(){
        return new Random()
                //元素个数
                // 这波操作同 .ints(10)
                .ints()
                .limit(10)

                .sorted()
        ;
    }

    static void testCollect(){

        /*
        LinkedList<String> ll =
        strings
                .stream()
                .collect(
                        // 感觉在归约前做处理(map)不是很好
                        // 一般都是在上游先map吧...
*/
/*
                        Collectors.mapping(
                                ele -> ele.toUpperCase(),
                                //joining 就是返回一段字符串
                                Collectors.joining(",","【","】")
                        )
*//*


                        //注意hashset不是原序的
                        //Collectors.toSet()

                        //值得注意的是返回类型是LinkedList
                        // 但是编译时会以为是Collection,直接输出会报错
                        Collectors.toCollection(LinkedList::new)


                )
        ;

        System.out.println(ll);
*/

        Map<Integer,String> personMap = persons
                .stream()
                .collect(
                        Collectors.collectingAndThen(
                                //do collecting
                                Collectors.toMap(
                                        // stream中有重复的值，则转换会报IllegalStateException异常
                                        // 返回值本身
                                        //Function.identity(),
                                        //Function::identity
                                        Person::getNo,
                                        Person::getName,
                                        //第三个参数mergeFunction，来解决冲突的问题
                                        //java.util.function.BiFunction.apply
                                        //入参是两个value
                                        (item, identicalItem) -> item.length() < identicalItem.length() ? item : identicalItem
                                ),
                                //then...
                                map -> {
                                    map.put(0,"westbrook");
                                    return map;
                                }

                                //这个故事告诉我们，跟stream相关的操作都是副本的
/*
                                map -> {
                                    map.forEach((k,v)->{
                                        //v = v.concat("(长度"+v.length()+")");
                                        v = "1";
                                    });
                                    return map;
                                }
*/
                        )
                )
        ;

        personMap.forEach((k,v)->System.out.println("key="+k+",value="+v));

        System.err.println("map元素个数->"+ personMap.values().stream().count());
        System.err.println("key的总和->"+ personMap
                .keySet()
                .stream()
                .collect(
                        Collectors.summingInt(value -> value)

                        // 返回的结果是一个统计类：
                        // IntSummaryStatistics{count=5, sum=56, min=0, average=11.200000, max=35}
                        //Collectors.summarizingInt(value -> value)

                        // 平均值
                        //Collectors.averagingInt(value -> value)

                        // 最值
                        // 这种情况下返回的结果是Optional[最大的元素]
/*
                        Collectors.maxBy(
                                //Comparator.naturalOrder()
                                (o1, o2) -> Math.max(obtainPreInt(o1),obtainPreInt(o2))
                        )
*/
                )
        );

        //Map<分组key,分组后的集合Collection>
        //像下面这个样例，将返回Map<String,Set<Person>>
        System.err.println("分组后的Map -> "+persons
                .stream()
                .collect(
                        Collectors.groupingBy(
                                Person::getName,
                                Collectors.toSet()
                        )
                )
        );

        //分组可以理解为特殊的分组
        //分组将产生一个Map<过滤条件Boolean,分区后的集合Collection>
        Map<Boolean, Map<String, Integer>> partitionedMap = persons
                .stream()
                .collect(
                        Collectors.partitioningBy(
                                Person::isNoOverTen,
                                Collectors.toMap(
                                        Person::getName,
                                        Person::getNo
                                )
                        )
                )
        ;

        System.err.println("分区后的Map -> "+partitionedMap);

        System.err.println("降维后的map -> "+ JsonUtil.toText(partitionedMap
                .values()
                .stream()
                .flatMap(map -> map.keySet().stream())
                //stream -> array
                .toArray()
        )
    );
    }

    /**
     * <br/>function: 获取整数中最大一位
     * <br/>tip:
     *
     * @param: [s]
     * @return: int
     * @author: weng
     * @date: 2022/5/8 14:24
     */
    private static int obtainPreInt(int i){
        return Integer.parseInt(String.valueOf(i).substring(0,1));
    }

    static void testReduce() throws InterruptedException {
        System.out.println(persons.stream().map(Person::getNo).reduce(-1, (val1, val2) -> val1 + val2));
        System.err.println(persons.stream().map(Person::getNo).reduce(-1, (val1, val2) -> val1 + val2,(sum1,sum2) -> sum1 + sum2));
        Thread.sleep(3000);
        System.out.println("------------------------");
        System.out.println(persons.parallelStream().map(Person::getNo).reduce(-1, (val1, val2) -> val1 + val2,(sum1,sum2) -> sum1 + sum2));
    }
}

@Data
@AllArgsConstructor
@Builder
//必须要除Object以外的显式父类才能调用父类的...
//@EqualsAndHashCode(callSuper = true)
class Person implements Serializable {
    Integer no;
    String name;

    @Override
    public String toString() {
        return JsonUtil.toText(this);
    }

    /**
     * <br/>function: 球衣号码是否两位数
     * <br/>tip:
     *
     * @param: \
     * @return: boolean
     * @author: weng
     * @date: 2022/5/8 14:43
     */
    public boolean isNoOverTen(){
        return this.no >= 10;
    }
}
